/*
 * MPEGFrameHeader.h
 *         -- define frame header format for MPEG file
 *
 * Author: Qiu Shuang
 * Date: Mar 1st, 2011
 */
#ifndef MPEGFRAMEHEADER_H
#define MPEGFRAMEHEADER_H

#include "MPEGDecodable.h"
#include "metadata.h"

#include <fstream>

typedef struct {
    unsigned int long_win[LONGWIN_SCALEFACTABLE];
    unsigned int short_win[SHORTWIN_SCALEFACTABLE];
} ScalefactorBandIndex;

/* forward declaration */
class MPEGFrameData;

class MPEGFrameHeader : public MPEGDecodable{
    friend class MPEGFrameData;
public:
    /* constructor
     *     take a memory address to the buffer as input
     */
    MPEGFrameHeader(const unsigned char *buf);

    /* field accessor */
    unsigned int  synchCode();
    unsigned int  version();
    unsigned int  layerCode();
    bool errorProtected();
    unsigned int  bitRateInx();
    unsigned int  samplingFreqInx();
    unsigned int  paddingFlag();
    unsigned int  modeFlag();
    unsigned int  modeExt();
    bool copyright();
    bool original();
    unsigned int emphasisFlag();
    unsigned int frameSize();
    unsigned int longWinScalefactorBandBound(int subbandIndex);
    unsigned int shortWinScalefactorBandBound(int subbandIndex);


    unsigned int p_framesize();

    /* header frame specified MPEG decoder */
    int decode(const unsigned char *buf);

    /* header frame specified MPEG decoded content */
    const unsigned char *decodedContent() const;

    /* output header information to specified file */
    void printHeader(std::ofstream &output);

private:
    unsigned int _synch_code : 12;        /* 12-bit synch-code */
    unsigned int _version : 1;            /* 1 bit version information
                                           *     1 = MPEG1, 0 = MPEG2 */
    unsigned int _layer_code : 2;         /* 4-bit layer code, calculated
                                           * as 4 - layercode */
    unsigned int _errorprotected : 1;     /* 0 = CRC-protected, 1 = not protected */
    unsigned int _bitrate_index : 4;      /* index to a table containing 14 standard
                                           * bitrates; 0=nonstandard bitrate is used */
    unsigned int _samplingfreq_index : 2; /* index to a table containing allowed
                                           * sampling frequencies */
    unsigned int _padding_flag : 1;       /* 1=contains additional bytes to control
                                           * mean bitrate; 0=no additional bytes */
    unsigned int _extension : 1;
    unsigned int _mode_flag : 2;          /* indicates channel usage according to table */
    unsigned int _mode_extension : 2;   
    unsigned int _copyright_flag : 1;     /* 1=copyright protected, 0=not protected */
    unsigned int _original_flag : 1;      /* 1=original, 0=copy */
    unsigned int _emphasis_flag : 2;

    bool checkHeader();          /* verify frame header */

    static const int BITRATES[BITRATE_INX_COUNTS];
    static const double SAMPLINGRATES[SAMPLE_INX_COUNTS];    
    static const ScalefactorBandIndex SCALEFACTORBANDTABLE[];
};

/* trieval inlined frame header field accessor */
inline unsigned int MPEGFrameHeader::synchCode(){
    return _synch_code;
}

inline unsigned int MPEGFrameHeader::version(){
    return _version;
}

inline unsigned int MPEGFrameHeader::layerCode(){
    return (4 - _layer_code);
}

inline bool MPEGFrameHeader::errorProtected(){
    // 1 means no CRC protection, while 0 does
    return (bool)!_errorprotected;
}

inline unsigned int MPEGFrameHeader::bitRateInx(){
    return _bitrate_index;
}

inline unsigned int MPEGFrameHeader::samplingFreqInx(){
    return _samplingfreq_index;
}

inline unsigned int MPEGFrameHeader::paddingFlag(){
    return _padding_flag;
}

inline unsigned int MPEGFrameHeader::modeFlag(){
    return _mode_flag;
}

inline unsigned int MPEGFrameHeader::modeExt(){
    return _mode_extension;
}

inline bool MPEGFrameHeader::copyright(){
    return (bool)_copyright_flag;
}

inline bool MPEGFrameHeader::original(){
    return (bool)_original_flag;
}

inline unsigned int MPEGFrameHeader::emphasisFlag(){
    return _emphasis_flag;
}

inline unsigned int MPEGFrameHeader::frameSize(){
    unsigned int size = (unsigned int )(144 * BITRATES[_bitrate_index] /
        SAMPLINGRATES[_samplingfreq_index]);

    if((SAMPLINGRATES[_samplingfreq_index] == 44.1) && paddingFlag()){
        size++;
    }

    size -= 4;              /* eliminate header size */
    if(errorProtected()){
        size -= 2;          /* eliminate CRC checksum size */
    }

    return size;
}

inline unsigned int MPEGFrameHeader::longWinScalefactorBandBound(int subbandIndex){
    if(subbandIndex < LONGWIN_SCALEFACTABLE){
        return SCALEFACTORBANDTABLE[samplingFreqInx()].long_win[subbandIndex];
    }else{
        printf("Error: unknown subband index for long window: %d\n", subbandIndex);

        return 0;
    }
}

inline unsigned int MPEGFrameHeader::shortWinScalefactorBandBound(int subbandIndex){
    if(subbandIndex < SHORTWIN_SCALEFACTABLE){
        return SCALEFACTORBANDTABLE[samplingFreqInx()].short_win[subbandIndex];
    }else{
        printf("Error: unknown subband index for short window: %d\n", subbandIndex);

        return 0;
    }
}


//unsigned int
//MPEGFrameHeader::p_framesize() 
// {
//     unsigned int bytesize;
//     MPEGLayer lay = (MPEGLayer)layerCode();
//
//     if (lay==I) {
//         bytesize = (int) ((12 * bitrate()) / samplingrate());
//     } else {
//         bytesize = (int) ((144 * bitrate()) / samplingrate());
//     }
//     if (lay==III && (header.version^1)==MPEG2) {
//         bytesize /= 2; // 1 slot = 4 bit in layer III, MPEG2
//     }
//     if (samplingrate()==44.1 && header.padding) {
//         bytesize++;
//     }
//     if (lay==I) {  // 1 slot = 1 byte in layer II, III
//         bytesize *= 4; // 1 slot = 4 bytes in layer I
//     }
//     bytesize -= 4; // deduct header size
//     if ( !header.errorprotection ) { // deduct crc size
//         bytesize -= 2;
//     }
//
//     return bytesize; // framesize as number of bytes
// }

#endif